<template>
  <div class="root">
    <div class="header-wrapper">
      <div class="header">
        <span>
          <SvgIcon
            name="icon-back"
            v-if="fileTree.length"
            style="cursor: pointer"
            @click="handleBackClick"
          />
          拓展应用工程项目资料</span
        >
        <div class="header-controls">
          <el-autocomplete
            v-if="fileTree.length"
            v-model="searchText"
            placeholder="搜索"
            clearable
            value-key="name"
            :fetch-suggestions="querySearch"
            style="width: 300px; margin-right: 16px"
            size="large"
            @select="handleSelect"
          >
            <template #prefix>
              <el-icon style="vertical-align: middle; color: #000">
                <Search />
              </el-icon>
            </template>
          </el-autocomplete>
          <el-select
            v-if="fileTree.length"
            v-model="columns"
            placeholder="显示列数"
            size="large"
            @change="updateColumns"
            style="width: 80px"
          >
            <el-option
              v-for="col in columnOptions"
              :key="col.value"
              :label="col.label"
              :value="col.value"
            />
          </el-select>
        </div>
      </div>
    </div>
    <div class="main">
      <template v-if="!fileTree.length">
        <h1>选择本地目录</h1>
        <el-divider style="margin: 16px 0"></el-divider>
        <div class="content-wrapper">
          <!-- 左侧文件选择区域 -->
          <div class="file-select-area">
            <div class="upload-zone" @click="triggerFileSelect">
              <div class="upload-icon">
                <el-icon style="font-size: 78px; color: #3498db"><UploadFilled /></el-icon>
              </div>
              <p>点击选择文件夹</p>
              <input
                type="file"
                ref="fileInput"
                style="display: none"
                directory
                webkitdirectory
                multiple
                @change="handleFilesSelected"
              />
            </div>
            <el-checkbox v-model="notShowHiddenFiles" style="margin-top: 12px"
              >不显示隐藏文件/目录（.开头的文件/目录）</el-checkbox
            >
          </div>

          <!-- 右侧提示区域 -->
          <div class="tips-area">
            <h3>重要提示：</h3>
            <p>请按照所示目录结构整理，确保在专业工作目录并包含必要的文件上传。</p>
            <h4>目录结构示例：</h4>
            <div class="directory-structure">
              <div class="dir">项目</div>
              <div class="subdir">├─ 目录1/</div>
              <div class="file">│ ├─ xxxx1.docx</div>
              <div class="file">│ ├─ xxxx1.pdf</div>
              <div class="file">│ └─ 子目录/</div>
              <div class="subdir">├─ 目录2/</div>
              <div class="file">│ ├─ xxxx2.docx</div>
              <div class="file">│ ├─ xxxx2.xlsx</div>
              <div class="file">│ └─ xxxx2.pdf</div>
              <div class="subdir">└─ 目录3/</div>
              <div class="file">└─ xxxx3.pdf</div>
            </div>
          </div>
        </div>
      </template>

      <template v-else>
        <!-- 渲染目录树 -->
        <div class="tree-wrapper">
          <div v-for="tree in fileTree" :key="tree.label" class="tree-item">
            <h3>{{ tree.label }}</h3>
            <el-tree
              class="treeCss"
              highlight-current
              style="max-width: 600px"
              :data="tree.children"
              :props="defaultProps"
              node-key="path"
              :default-expanded-keys="defaultExpandedKeys"
              @node-click="handleNodeClick"
            >
              <template #default="{ node }">
                <!-- 检查配置文件是否包含该文件 -->
                <el-tooltip
                  v-if="isFileConfigured(node.label)"
                  effect="dark"
                  :content="getFileConfig(node.label)"
                  placement="top-start"
                >
                  <div>
                    <SvgIcon :name="getIconName(node)" style="margin-right: 4px" />
                    <span
                      class="font-weight-bold"
                      :title="node.label"
                      v-html="highlightText(node.label)"
                    ></span>
                  </div>
                </el-tooltip>
                <template v-else>
                  <SvgIcon :name="getIconName(node)" style="margin-right: 4px" />
                  <span
                    class="font-weight-bold"
                    :title="node.label"
                    v-html="highlightText(node.label)"
                  ></span>
                </template>
              </template>
            </el-tree>
          </div>
        </div>
      </template>
    </div>
  </div>
</template>

<script setup>
import { Search, UploadFilled } from '@element-plus/icons-vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'

const searchText = ref('')

const fileInput = ref(null)

const allFiles = ref([])
const allFileNames = ref([])
const fileTree = ref([])
const defaultProps = {
  children: 'children',
  label: 'label',
}

// 默认展开的节点key数组
const defaultExpandedKeys = ref([])

// 计算所有三级以内的节点路径
const calculateExpandedKeys = (treeData, currentLevel = 0) => {
  const keys = []

  if (!treeData || !Array.isArray(treeData)) return keys

  treeData.forEach((node) => {
    const nodePath = node.path

    // 如果是三级以内的目录节点，添加到展开列表
    if (currentLevel < 2 && node.children && node.children.length > 0) {
      keys.push(nodePath)
      // 递归处理子节点
      const childKeys = calculateExpandedKeys(node.children, currentLevel + 1)
      keys.push(...childKeys)
    }
  })

  return keys
}

// 监听fileTree变化，重新计算展开节点
watch(
  () => fileTree.value,
  (newTree) => {
    if (newTree && newTree.length > 0) {
      defaultExpandedKeys.value = calculateExpandedKeys(newTree)
    }
  },
  { deep: true },
)

const notShowHiddenFiles = ref(true)

// 列数相关设置
const columns = ref(2) // 默认显示4列
const columnOptions = [
  { label: '1列', value: 1 },
  { label: '2列', value: 2 },
  { label: '3列', value: 3 },
  { label: '4列', value: 4 },
  { label: '5列', value: 5 },
]

// 计算每个树项的宽度百分比
const treeItemWidth = computed(() => {
  const gapWidth = 12 // 对应CSS中的gap计算值
  return `calc(${100 / columns.value}% - ${((columns.value - 1) * gapWidth) / columns.value}px)`
})

// 更新列数的处理函数
const updateColumns = () => {
  // 列数改变时会自动通过computed属性更新样式
  console.log('显示列数已更新为:', columns.value)
}

// 用于存储配置文件内容
const configData = ref([])

// 高亮文本函数
const highlightText = (text) => {
  if (!searchText.value) {
    return escapeHTML(text)
  }
  // 转义正则特殊字符
  const escapedQuery = searchText.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  // 创建正则表达式，忽略大小写
  const regex = new RegExp(`(${escapedQuery})`, 'gi')
  // 替换匹配的文本为高亮样式
  return escapeHTML(text).replace(regex, '<span class="highlight">$1</span>')
}

// 读取配置文件函数
const loadConfig = async () => {
  try {
    // 在Vue中，public目录下的文件可以通过根路径直接访问
    const response = await fetch('/config.json')
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }
    configData.value = await response.json()
    return configData.value
  } catch (error) {
    console.error('加载配置文件失败:', error)
    return []
  }
}

// 检测配置文件中是否包含指定文件
const isFileConfigured = (fileName) => {
  return configData.value.some((item) => item.name === fileName)
}

// 根据文件名获取文件的配置信息
const getFileConfig = (fileName) => {
  const file = configData.value.find((item) => item.name === fileName)
  if (!file) {
    return ''
  }
  return file.description || ''
}

// 组件挂载时加载配置文件
onMounted(() => {
  loadConfig()
})

const triggerFileSelect = () => {
  fileInput.value.click()
}
// 存储所有创建的Blob URL，用于清理
const blobUrls = new Set()

// 组件卸载时移除事件监听器
onUnmounted(() => {
  // 清理所有Blob URL和缓存
  try {
    blobUrls.forEach((url) => {
      URL.revokeObjectURL(url)
    })
    blobUrls.clear()
  } catch (e) {
    console.error('组件卸载时资源清理失败:', e)
  }
})

const handleNodeClick = (data) => {
  // 如果点击的是文件节点且有文件引用
  if (data.isFile && data.file) {
    const file = data.file
    const reader = new FileReader()

    // 根据文件类型决定预览方式
    const fileExtension = file.name.split('.').pop().toLowerCase()
    const iOfficeFile = ['docx', 'xlsx'].includes(fileExtension)
    const isPdfFile = fileExtension === 'pdf'
    const isTextFile = ['txt', 'js', 'html', 'css', 'json', 'md', 'xml'].includes(fileExtension)
    const isImageFile = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(fileExtension)

    if (iOfficeFile) {
      // 创建Blob对象和URL（用于初始加载）
      const blob = new Blob([file], {
        type: file.type,
      })
      const fileUrl = URL.createObjectURL(blob)

      const path = fileExtension === 'docx' ? '#/v1/word' : '#/v1/excel'

      // 打开新标签页并导航到DocView页面，通过URL参数传递文件ID和初始Blob URL
      window.open(
        `/${path}?fileName=` +
          encodeURIComponent(file.name) +
          // '&fileId=' +
          // fileId +
          '&fileUrl=' +
          fileUrl,
        '_blank',
      )
      // 存储Blob URL以便后续清理
      blobUrls.add(fileUrl)
    } else if (isTextFile) {
      // 读取文本文件内容
      reader.onload = (e) => {
        const content = e.target.result
        // 创建新标签页并显示文本内容
        const newTab = window.open('', '_blank')

        // 创建HTML结构
        const html = newTab.document.createElement('html')

        // 创建head部分
        const head = newTab.document.createElement('head')
        const title = newTab.document.createElement('title')
        title.textContent = file.name

        const style = newTab.document.createElement('style')
        style.textContent = `
          body { font-family: monospace; padding: 20px; white-space: pre-wrap; }
          h1 { margin-top: 0; }
        `

        head.appendChild(title)
        head.appendChild(style)

        // 创建body部分
        const body = newTab.document.createElement('body')
        const h1 = newTab.document.createElement('h1')
        h1.textContent = file.name

        const pre = newTab.document.createElement('pre')
        pre.textContent = escapeHTML(content)

        body.appendChild(h1)
        body.appendChild(pre)

        // 组装文档
        html.appendChild(head)
        html.appendChild(body)

        // 清空现有内容并添加新内容
        newTab.document.open()
        newTab.document.appendChild(html)
        newTab.document.close()
      }
      reader.readAsText(file)
    } else if (isImageFile) {
      // 读取图片文件并预览
      reader.onload = (e) => {
        const imageUrl = e.target.result
        const newTab = window.open('', '_blank')

        // 创建HTML结构
        const html = newTab.document.createElement('html')

        // 创建head部分
        const head = newTab.document.createElement('head')
        const title = newTab.document.createElement('title')
        title.textContent = file.name

        const style = newTab.document.createElement('style')
        style.textContent = `
          body { display: flex; justify-content: center; align-items: center;flex-direction: column; min-height: 100vh; margin: 0; background: #f0f0f0; }
          img { max-width: 90%; max-height: 90%; }
        `

        head.appendChild(title)
        head.appendChild(style)

        // 创建body部分
        const body = newTab.document.createElement('body')
        const img = newTab.document.createElement('img')
        img.src = imageUrl
        img.alt = file.name

        body.appendChild(img)

        // 组装文档
        html.appendChild(head)
        html.appendChild(body)

        // 清空现有内容并添加新内容
        newTab.document.open()
        newTab.document.appendChild(html)
        newTab.document.close()
      }
      reader.readAsDataURL(file)
    } else if (isPdfFile) {
      // 对于PDF文件，创建带有正确标题的HTML页面来嵌入PDF
      const blob = new Blob([file], { type: file.type || 'application/pdf' })
      const url = URL.createObjectURL(blob)

      // 打开新标签页（空白页）
      const newTab = window.open('', '_blank')
      if (!newTab) {
        alert('弹窗被拦截，请允许弹窗后重试')
        return
      }
      // 1. 操作新标签页的document
      const doc = newTab.document
      const win = newTab

      // 2. 设置页面标题（直接操作title元素）
      doc.title = escapeHTML(file.name)

      // 3. 创建并添加样式（替代<style>标签）
      const style = doc.createElement('style')
      style.textContent = `
        body {
          margin: 0;
          padding: 0;
          height: 100vh;
          display: flex;
          flex-direction: column;
          font-family: Arial, sans-serif;
        }
        .pdf-container {
          flex: 1;
          overflow: hidden;
        }
        iframe {
          width: 100%;
          height: 100%;
          border: none;
        }
      `
      doc.head.appendChild(style) // 将样式添加到<head>

      // 4. 创建body内容（容器div和iframe）
      const body = doc.body

      // 创建PDF容器div
      const pdfContainer = doc.createElement('div')
      pdfContainer.className = 'pdf-container' // 添加类名

      // 创建iframe用于预览PDF
      const iframe = doc.createElement('iframe')
      iframe.src = url // 绑定Blob URL
      iframe.title = escapeHTML(file.name) // 设置iframe标题
      iframe.style.width = '100%'
      iframe.style.height = '100%'
      iframe.style.border = 'none'

      // 组装结构：iframe -> 容器div -> body
      pdfContainer.appendChild(iframe)
      body.appendChild(pdfContainer)

      // 可选：释放Blob URL（如果确认不再需要）
      // 注意：需在新标签页关闭后释放，可监听新标签页关闭事件
      win.addEventListener('beforeunload', () => {
        URL.revokeObjectURL(url)
      })
      // 存储Blob URL以便后续清理
      blobUrls.add(url)
    } else {
      // 对于其他类型的文件，尝试使用浏览器默认处理
      reader.onload = (e) => {
        const blob = new Blob([e.target.result], { type: file.type })
        const url = URL.createObjectURL(blob)
        window.open(url, '_blank')
      }
      reader.readAsArrayBuffer(file)
    }
  }
}

// HTML转义函数，防止XSS攻击
function escapeHTML(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;')
}
const handleFilesSelected = (event) => {
  allFiles.value = Array.from(event.target.files)
  allFileNames.value = allFiles.value.map((file) => ({
    name: file.name,
    path: file.webkitRelativePath,
  }))

  fileTree.value = buildFileTree(allFiles.value)
}

// 构建文件树结构
const buildFileTree = (files) => {
  // files数组按webkitRelativePath排序
  files.sort((a, b) => a.webkitRelativePath.localeCompare(b.webkitRelativePath))
  // 过滤掉以webkitRelativePath中包含"."的文件和目录
  if (notShowHiddenFiles.value) {
    files = files.filter((file) => !file.webkitRelativePath.includes('/.'))
  }
  // 创建根对象，使用数组存储children
  const root = { label: '/', path: '/', children: [] }
  const rootMap = {} // 用于快速查找子节点

  for (const file of files) {
    // 获取相对路径（如 "docs/note.txt"）
    const relativePath = file.webkitRelativePath
    const parts = relativePath.split('/') // 拆分路径为数组 ["docs", "note.txt"]

    let current = root
    let currentMap = rootMap
    let currentPath = ''

    parts.forEach((part, index) => {
      // 最后一个部分是文件名，前面的是文件夹
      const isFile = index === parts.length - 1

      // 构建当前节点的完整路径
      currentPath = index === 0 ? part : `${currentPath}/${part}`

      if (!currentMap[part]) {
        // 创建新节点，包含完整路径
        const newNode = {
          label: part,
          path: currentPath,
          isFile: isFile,
        }

        // 如果是文件节点，保留原始File对象引用
        if (isFile) {
          newNode.file = file
        }
        // 非文件节点需要有children数组
        if (!isFile) {
          newNode.children = []
        }

        // 添加到父节点的children数组
        current.children.push(newNode)

        // 更新映射和当前节点
        currentMap[part] = { node: newNode, map: {} }
      }

      current = currentMap[part].node
      currentMap = currentMap[part].map
    })
  }
  // 返回根节点的children数组
  allFiles.value = []
  return root.children[0].children
}

// 根据node节点是否文件及其后缀判断icon
const getIconName = (node) => {
  if (node.data.isFile) {
    const fileExtension = node.label.split('.').pop().toLowerCase()
    const iOfficeFile = fileExtension === 'docx'
    const isXlsxFile = fileExtension === 'xlsx'
    const isPdfFile = fileExtension === 'pdf'
    const isTextFile = ['txt', 'js', 'html', 'css', 'json', 'md', 'xml'].includes(fileExtension)
    const isImageFile = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'].includes(fileExtension)
    switch (true) {
      case iOfficeFile:
        return 'icon-file_type_word'
      case isXlsxFile:
        return 'icon-file_type_excel'
      case isPdfFile:
        return 'icon-file_type_pdf'
      case isTextFile:
        return 'icon-file_type_text'
      case isImageFile:
        return 'icon-file_type_image'
      default:
        return 'icon-default_file'
    }
  } else {
    return 'icon-default_folder'
  }
}

// 搜索建议
const querySearch = (queryString, callback) => {
  const results = queryString
    ? allFileNames.value.filter(createFilter(queryString))
    : allFileNames.value
  callback(results)
}
const createFilter = (queryString) => {
  return (file) => {
    return file.path.toLowerCase().indexOf(queryString.toLowerCase()) !== -1
  }
}

// 处理选择事件
const handleSelect = (node) => {
  // 根据node.path，在fileTree里找到对应文件的引用
  const { file } = findFileByPath(fileTree.value, node.path)
  if (file) {
    handleNodeClick({
      isFile: true,
      file: file,
    })
  } else {
    console.error('未找到对应文件:', node.path)
  }
}
// 递归查找文件节点
const findFileByPath = (nodes, path) => {
  for (const node of nodes) {
    if (node.path === path) {
      return node
    }
    if (node.children) {
      const found = findFileByPath(node.children, path)
      if (found) {
        return found
      }
    }
  }
  return null
}

// 点击返回上一级目录
const handleBackClick = () => {
  searchText.value = ''
  fileInput.value = null
  allFiles.value = []
  fileTree.value = []
  allFileNames.value = []
}
</script>

<style lang="scss" scoped>
.root {
  --tw-ring-offset-shadow: 0 0 #0000;
  --tw-ring-shadow: 0 0 #0000;
  --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

@media (min-width: 640px) {
  .header,
  .main {
    max-width: 640px;
  }
}

@media (min-width: 768px) {
  .header,
  .main {
    max-width: 768px;
  }
}

@media (min-width: 1024px) {
  .header,
  .main {
    max-width: 1024px;
  }
}

@media (min-width: 1280px) {
  .header,
  .main {
    max-width: 1280px;
  }
}

@media (min-width: 1536px) {
  .header,
  .main {
    max-width: 1536px;
  }
}

.header-wrapper {
  position: sticky;
  top: 0;
  background-color: white;
  box-shadow:
    var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
  display: flex;
  align-items: center;
  justify-content: space-between;
  z-index: 1000;
}

.header {
  width: 100%;
  padding: 1rem;
  display: flex;
  justify-content: space-between;
  margin: 0 auto;
  color: rgb(31 41 55);
  font-weight: 700;
  font-size: 1.25rem;
  line-height: 1.75rem;
}

.header-controls {
  display: flex;
  align-items: center;
  gap: 16px;
}

.main {
  margin: 0 auto;
  padding: 2rem;
  margin-top: 24px;
  color: #333;
  width: 100%;
  background-color: white;
  box-shadow:
    var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
  h1 {
    font-size: 1.8rem;
    margin-bottom: 2rem;
    color: #333;
  }

  .content-wrapper {
    display: flex;
    gap: 4rem;
    align-items: center;

    @media (max-width: 768px) {
      flex-direction: column;
    }
  }

  .file-select-area {
    flex: 1;

    .upload-zone {
      border: 2px dashed #ddd;
      border-radius: 8px;
      padding: 6rem 1rem;
      text-align: center;
      cursor: pointer;
      transition: all 0.3s ease;

      &:hover {
        border-color: #3498db;
        background-color: #f9f9f9;
      }

      .upload-icon {
        margin-bottom: 1rem;
      }

      p {
        color: #666;
        font-size: 1rem;
      }
    }
  }

  .tips-area {
    flex: 1;
    max-width: 600px;

    h3 {
      color: #e74c3c;
      font-size: 1.2rem;
      margin-bottom: 1rem;
    }

    h4 {
      color: #333;
      font-size: 1rem;
      margin-top: 1.5rem;
      margin-bottom: 0.5rem;
    }

    p {
      color: #666;
      line-height: 1.6;
      margin-bottom: 1rem;
    }

    .directory-structure {
      font-family: 'Courier New', monospace;
      font-size: 0.9rem;
      line-height: 1.5;
      padding: 1rem;
      background-color: #f5f5f5;
      border-radius: 4px;
      white-space: pre-wrap;

      .dir {
        font-weight: bold;
        color: #3498db;
      }

      .subdir {
        color: #2ecc71;
      }

      .file {
        color: #333;
      }
    }
  }

  .tree-wrapper {
    display: flex;
    flex-wrap: wrap;
    // justify-content: space-between;
    gap: 12px;
  }

  .tree-item {
    width: v-bind(treeItemWidth);
    // min-width: 300px;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 1rem;
    box-sizing: border-box;
    overflow-x: auto;
  }
}

.font-weight-bold {
  font-weight: 500;
  color: #333;
  display: inline-block;
  flex: 1;
  max-width: calc(100% - 18px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 8px 0;
}

// 高亮样式
:deep(.highlight) {
  background-color: #ffeb3b;
  color: #000;
  border-radius: 2px;
  // padding: 0 2px;
  // font-weight: bold;
}
</style>
